<?php
/**
* DIXon Generation 1 - JavaScript Client Pages
* 
* PHP Bindings for PHP 5.2.x and above
* 
* Copyright (c) 2008, Artem Ploujnikov
* All rights reserved.
* 
* Redistribution and use in source and binary forms, with or without modification, 
* are permitted provided  that the following conditions are met:
* 
*  * Redistributions of source code must retain the above copyright notice, this 
*    list of conditions and the following disclaimer.
*  * Redistributions in binary form must reproduce the above copyright notice, this 
* 	 list of conditions and the following disclaimer in the documentation and/or 
*    other materials provided with the distribution.
* *  Neither the name of Artem Ploujnikov nor the names of the project's contributors 
* 	 may be used to endorse or promote products derived from this software without 
*    specific prior written permission.
* 
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
* IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
* POSSIBILITY OF SUCH DAMAGE. 
*/


/**
* Represents an error that occurred on a JSCP page.
*/
class DiXon_PageError implements IScriptable {
	/**
	* Indicates that the error occurred in the 
	* DiXon compiler
	*/
	const SourceCompiler = 'Compiler';
	
	/**
	* Indicates that the error occurred within
	* the client-side page
	*/
	const SourceClientPage = 'ClientPage';
	
	const DisplayStart = ' JSClientPageError.display(';
	const DisplayEnd = '); ';
	
	
	/**
	* Indicates whether the error page should be used for PHP errors
	* 
	* @var bool
	*/
	private static $errorPageEnabled = false;
	
	/**
	* Indicates where the error occurred
	* 
	* @var string
	*/
	private $source;
	
	/**
	* The message to output
	* 
	* @var string
	*/
	private $message;
	
	
	/**
	* The stack trace for the error, 
	* if available
	* 
	* @var array
	*/
	private $stackTrace;
	
	/**
	* Creates a new page error
	* 
	* @param string $source    indicates where the error was created (one of the SOURCE_* constants)
	* @param string $message   the error message
	* @param array $stackTrace the stack trace, as an array of DiXon_StackFrame objects (optional)
	*/
	public function __construct($source, $message, $stackTrace = null) {
		$this->source = $source;
		$this->message = $message;
		$this->stackTrace = $stackTrace;		
	}

	/**
	* Converts a PHP exception to a page error, which will be output
	* directly in the client's browser, replacing any content that was
	* previously output, unless otherwise specified
	* 
	* @return DiXon_PageError
	*/
	public static function fromException(Exception $exception) {
		return new DiXon_PageError(
			self::SourceCompiler,
			$exception->getMessage(),			
			array_map(
				array('DiXon_StackFrame', 'toFrame'),
				$exception->getTrace()
			)			
		);
	}
	
	/**
	* Emits the code that displays this error page in the user's browser.
	*/
	public function displayInScript(DiXon_ScriptWriter $writer) {
		$writer
			->writeRaw(self::DisplayStart)
			->writeItem($this)
			->writeRaw(self::DisplayEnd);
		
	}
	
	/**
	* Outputs this error to a script writer as an anonymous object
	* that can be used to create and display a JSClientPageError
	* 
	* @param DiXon_ScriptWriter $writer a DiXon script writer instance
	*/
	public function writeScript(DiXon_ScriptWriter $writer) {
		$writer
			->startObject()
			->writeKeyValuePair('message', $this->message)
			->endItem()
			->writeKeyValuePair('source', $this->source)
			->endItem()
			->writeKey('stackTrace')
			->writeItems($this->stackTrace)
			->endObject();
	}
	
	/**
	* Indicates whether or not the PHP error handler will use the 
	* error page. It cannot be used if the error occurs before the
	* page was compiled.
	* 
	* @return bool
	*/
	public static function isErrorPageEnabled() {
		return self::$errorPageEnabled;
	}
	
	/**
	* Enables the use of the error page
	* 
	*/
	public static function enableErrorPage() {
		self::$errorPageEnabled = true;
	}
	
	/**
	* Handles an old-school PHP error (one that is not dispatched by means of
	* an exception)
	* 
	* @param int   $errno      the PHP error number
	* @param mixed $errstr	   the error message
	* @param mixed $errfile    the file where the error occurred
	* @param mixed $errline	   the line where the error occurred
	* @param mixed $errcontext contains all variables in the scope where the error
	* 		was generated (not used)
	*/
	public static function handlePhpError($errno, $errstr, $errfile, $errline, $errcontext = null) {
		$message = $errno . ': ' . $errstr . ' - ' . $errfile . '#' . $errline;
		$scriptWriter = new DiXon_ScriptWriter(new DiXon_StandardOutputStream());
		if (self::$errorPageEnabled) {
			$error = new DiXon_PageError(
				DiXon_PageError::SourceCompiler,
				$message,
				array_map(
					array('DiXon_StackFrame', 'toFrame'),
					debug_backtrace()
				)			
			);				
			$error->displayInScript($scriptWriter);
		} else {			
			$scriptWriter->callMethod('alert', array($message));
		}
	}
}

?>